﻿using ProgressTracker.Account.Infrastructure;
using ProgressTracker.Account.Messages;
using ProgressTracker.Data.Abstract;
using ProgressTracker.Data.Entities;
using System;

namespace ProgressTracker.Account.Services
{
    public interface IAccountService
    {
        bool AccountExists(string email);
        LoginResult Login(string email, string password);
        AccountCreateResult CreateAccount(string email, string password);
        ResetPasswordResult ResetPassword(string email);
        ChangePasswordResult ChangePassword(string email, string password);
    }

    public class AccountService : IAccountService
    {
        private const int NumberOfRounds = 10;
        private IUserRepository _userRepo;
        private ICrypt _cryptServices;
        private IPasswordGenerator _passwordGenerator;

        public AccountService(IUserRepository userRepo, ICrypt cryptServices, IPasswordGenerator passwordGenerator)
        {
            _userRepo = userRepo;
            _cryptServices = cryptServices;
            _passwordGenerator = passwordGenerator;
        }

        public bool AccountExists(string email)
        {
            return _userRepo.UserExists(email);
        }

        public LoginResult Login(string email, string password)
        {
            string message;
            LoginResultCode resultCode;
            UserLogin userLogin = null;

            if (!_userRepo.UserExists(email))
            {
                message = "User does not exist.";
                resultCode = LoginResultCode.AccountDoesNotExist;
            }
            else
            {
                var user = _userRepo.GetUserByEmail(email);

                if (_cryptServices.CheckPassword(password, user.PasswordHash))
                {
                    message = "Success";
                    resultCode = LoginResultCode.Success;
                    userLogin = new UserLogin
                    {
                        Email = email,
                        MustChangePassword = user.MustChangePassword
                    };
                }
                else
                {
                    message = "Passwords do not match";
                    resultCode = LoginResultCode.PasswordDoesNotMatch;
                }
            }

            return new LoginResult
            {
                Message = message,
                ResultCode = resultCode,
                User = userLogin
            };
        }

        public AccountCreateResult CreateAccount(string email, string password)
        {
            if(_userRepo.UserExists(email))
            {
                return new AccountCreateResult
                {
                    Message = "User already exists.",
                    ResultCode = AccountResultCode.UserAlreadyExists
                };
            }

            try
            {
                var salt = _cryptServices.GenerateSalt(NumberOfRounds);
                var hashedPass = _cryptServices.HashPassword(password, salt);

                _userRepo.Create(new User
                    {
                        Email = email,
                        PasswordHash = hashedPass
                    });

                return new AccountCreateResult
                {
                    Message = "Success",
                    ResultCode = AccountResultCode.Success
                };
            }
            catch (Exception ex)
            {
                return new AccountCreateResult
                {
                    Message = ex.Message,
                    ResultCode = AccountResultCode.FailedToCreate
                };
            }
        }

        public ResetPasswordResult ResetPassword(string email)
        {
            var newPass = _passwordGenerator.GenerateNewPassword();
            
            var user = _userRepo.GetUserByEmail(email);
            user.MustChangePassword = true;

            var salt = _cryptServices.GenerateSalt(NumberOfRounds);
            user.PasswordHash = _cryptServices.HashPassword(newPass, salt);

            _userRepo.Save(user);

            return new ResetPasswordResult
            {
                NewPassword = newPass
            };
        }

        public ChangePasswordResult ChangePassword(string email, string password)
        {
            
            var user = _userRepo.GetUserByEmail(email);

            var salt = _cryptServices.GenerateSalt(NumberOfRounds);
            user.PasswordHash = _cryptServices.HashPassword(password, salt);
            user.MustChangePassword = false;

            _userRepo.Save(user);

            return new ChangePasswordResult
            {
                Success = true,
                Message = "Success!"
            };
        }
    }
}
